package com.glowdayz.common;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.BufferedHttpEntity;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;

import android.util.Log;

public class HttpHandler implements Serializable {
	
	private static final long serialVersionUID = 1L;

	static String TAG = HttpHandler.class.getSimpleName();

	// 기본 인코딩 형식 지정
	private static final String ENC_TYPE = "UTF-8";

	// 기본 접속 제한 시간 (1/1000초 단위 --> 밀리세컨드)
	private static final int TIMEOUT = 30000;

	private static HttpHandler current = null;

	public static HttpHandler getInstance() {
		if (current == null) {
			current = new HttpHandler();
		}

		return current;
	}

	public static void freeInstance() {
		if (current != null) {
			current = null;
		}
	}

	/** 접속의 기본 정보를 내장하고 있는 객체를 생성하여 리턴 */
	private HttpClient getClient(String encType) {

		Log.d(TAG, "=========== method start >> getClient");

		// 접속을 하기 위한 기본 환경설정
		HttpParams params = new BasicHttpParams();
		// 웹 통신 프로토콜의 버전을 설정
		params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION,
				HttpVersion.HTTP_1_1);

		// 접속기능을 하는 객체 생성 <-- 예가 주인공
		HttpClient client = new DefaultHttpClient(params);

		// 접속 제한시간 설정 (30초 / 1초=1000)
		HttpParams p = client.getParams();
		HttpConnectionParams.setConnectionTimeout(p, TIMEOUT);

		// 응답 제한시간 설정
		HttpConnectionParams.setSoTimeout(p, TIMEOUT);

		// 통신에 사용될 인코딩 타입 설정
		if (encType == null) {
			// 값이 없다면 기본값으로 UTF-8 설정
			encType = ENC_TYPE;
		}
		HttpProtocolParams.setContentCharset(p, encType);

		Log.d(TAG, "=========== method end >> getClient");

		return client;
	}

	private String getUrl(String url) {
		String result = null;
		
		int p = url.indexOf("?");
		
		if (p > 0) {
			result = url.substring(0, p);
		} else {
			result = url;
		}
		
		Log.d(TAG, "[접속 URL] " + url);
		
		return result;
	}
	
	private ArrayList<String[]> getParams(String url) {
		ArrayList<String[]> params = new ArrayList<String[]>();
		
		int p = url.indexOf("?");
		
		if (p > 0) {			
			String queryString = url.substring(p + 1);
			String[] queryArray = queryString.split("&");
			
			for (int i=0; i<queryArray.length; i++) {
				String[] tmp = queryArray[i].split("=");
				if (tmp.length > 1) {
					params.add(tmp);
					Log.d(TAG, "[파라미터 + " + i + "] " + tmp[0] + "=" + tmp[1]);
				}
			}
		}

		return params;
	}

	/** 웹 서버에 요청을 보내고 응답을 리턴하는 메서드 - 1차 작업 */
	public InputStream get(String url, String encType) {

		Log.d(TAG, "=========== method start >> get");

		// 접속 주소의 앞뒤 공백 제거
		url = url.trim();

		// 응답결과가 저장될 객체 --> 리턴대상
		InputStream istream = null;
		
		// 주소를 파라미터와 URL로 분리
		String pageUrl = this.getUrl(url);
		ArrayList<String[]> params = this.getParams(url);
		String encodeUrl = pageUrl;
		
		if (params != null) {
			int size = params.size();
			
			if (size > 0) {
				encodeUrl += "?";
				
				for (int i=0; i<size; i++) {
					try {
						String[] tmp = params.get(i);
						encodeUrl += tmp[0] + "=" + URLEncoder.encode(tmp[1], encType);
						
						if (i+1 < size) {
							encodeUrl += "&";
						}
					} catch (UnsupportedEncodingException e) {}
				}
			}
		}

		// 통신정보 설정
		HttpClient client = this.getClient(encType);
		
		// 접속을 수행하기 위한 객체
		HttpGet httpget = new HttpGet(encodeUrl);

		try {
			// 요청을 보내고, 응답을 받는다.
			HttpResponse response = client.execute(httpget);

			// 서버의 정상 동작 여부 검사
			int resultCode = response.getStatusLine().getStatusCode();

			if (resultCode == HttpURLConnection.HTTP_OK) {
				// 수신된 응답에서 실 데이터를 추출한다.
				HttpEntity entity = response.getEntity();
				BufferedHttpEntity buffer = new BufferedHttpEntity(entity);

				// 추출한 데이터를 InputStream으로 변환
				istream = buffer.getContent();
			} else {
				// 서버 에러의 원인
				String reason = response.getStatusLine().getReasonPhrase();

				Log.e(TAG, "서버에러: " + resultCode);
				Log.e(TAG, reason);

			}
		} catch (ClientProtocolException e) {

			Log.e(TAG, "통신처리 에러: " + e.getLocalizedMessage());

			Log.e(TAG, e.getLocalizedMessage());
			Log.e(TAG, e.getStackTrace().toString());

		} catch (IOException e) {

			Log.e(TAG, "응답수신 에러: " + e.getLocalizedMessage());

			Log.e(TAG, e.getLocalizedMessage());
			Log.e(TAG, e.getStackTrace().toString());

		} catch (Exception e) {

			Log.e(TAG, "기타 통신 에러: " + e.getLocalizedMessage());

			Log.e(TAG, e.getLocalizedMessage());
			Log.e(TAG, e.getStackTrace().toString());

		} finally {
			// 통신 해제
			client.getConnectionManager().shutdown();
		}
		/********* 통신처리 끝 ********/

		Log.d(TAG, "=========== method end >> get");

		return istream;
	}
	
	public InputStream postMulti(String url, String encType, FileInfo[] files) {

		Log.d(TAG, "=========== method start >> post");

		// 접속 주소의 앞뒤 공백 제거
		url = url.trim();

		// 응답결과가 저장될 객체 --> 리턴대상
		InputStream istream = null;
		
		// 주소를 파라미터와 URL로 분리
		String pageUrl = this.getUrl(url);
		ArrayList<String[]> params = this.getParams(url);

		// 통신정보 설정
		HttpClient client = this.getClient(encType);
		
		// 접속을 수행하기 위한 객체
		HttpPost httppost = new HttpPost(pageUrl);
		
		// 업로드될 파일 정보가 있는 경우
		if (files != null) {
			MultipartEntity entity = new MultipartEntity();

			for (int i=0; i<files.length; i++) {
				if (files[i] != null) {
					Log.d(TAG, "[UPLOAD INFO] " + files[i].getName() + "=" + files[i].getPath());
					entity.addPart(files[i].getName(), new FileBody(new File(files[i].getPath())));
				}
			}

			if (params != null) {
				for (int i=0; i<params.size(); i++) {
					String[] tmp = params.get(i);
					try {
						entity.addPart(tmp[0], new StringBody(tmp[1], Charset.forName(encType)));
					} catch (UnsupportedEncodingException e) {
						e.printStackTrace();
					}
				}
			}

			httppost.setEntity(entity);
		}
		// 업로드 파일 정보는 없고, 파라미터만 있는 경우.
		else {
			if (params != null) {
				int size = params.size();
				
				if (size > 0) {
					List<NameValuePair> nvp = new ArrayList<NameValuePair>();
					
					for (int i=0; i<size; i++) {
						String[] tmp = params.get(i);
						nvp.add(new BasicNameValuePair(tmp[0], tmp[1]));
					}
					
					try {
						UrlEncodedFormEntity encEntity = new UrlEncodedFormEntity(nvp, encType);
						httppost.setEntity(encEntity);
					} catch (UnsupportedEncodingException e) {}
				}
			}
		}

		try {
			// 요청을 보내고, 응답을 받는다.
			HttpResponse response = client.execute(httppost);

			// 서버의 정상 동작 여부 검사
			int resultCode = response.getStatusLine().getStatusCode();

			if (resultCode == HttpURLConnection.HTTP_OK) {
				// 수신된 응답에서 실 데이터를 추출한다.
				HttpEntity entity = response.getEntity();
				BufferedHttpEntity buffer = new BufferedHttpEntity(entity);

				// 추출한 데이터를 InputStream으로 변환
				istream = buffer.getContent();
			} else {
				// 서버 에러의 원인
				String reason = response.getStatusLine().getReasonPhrase();

				Log.e(TAG, "서버에러: " + resultCode);
				Log.e(TAG, reason);

			}
		} catch (ClientProtocolException e) {

			Log.e(TAG, "통신처리 에러: " + e.getLocalizedMessage());

			Log.e(TAG, e.getLocalizedMessage());
			Log.e(TAG, e.getStackTrace().toString());

		} catch (IOException e) {

			Log.e(TAG, "응답수신 에러: " + e.getLocalizedMessage());

			Log.e(TAG, e.getLocalizedMessage());
			Log.e(TAG, e.getStackTrace().toString());

		} catch (Exception e) {

			Log.e(TAG, "기타 통신 에러: " + e.getLocalizedMessage());

			Log.e(TAG, e.getLocalizedMessage());
			Log.e(TAG, e.getStackTrace().toString());

		} finally {
			// 통신 해제
			client.getConnectionManager().shutdown();
		}
		/********* 통신처리 끝 ********/

		Log.d(TAG, "=========== method end >> post");

		return istream;
	}
	
	public InputStream postSingle(String url, String encType, FileInfo file) {

		Log.d(TAG, "=========== method start >> post");
		
		// 접속 주소의 앞뒤 공백 제거
		url = url.trim();
		
		// 응답결과가 저장될 객체 --> 리턴대상
		InputStream istream = null;
		
		// 주소를 파라미터와 URL로 분리
		String pageUrl = this.getUrl(url);
		ArrayList<String[]> params = this.getParams(url);
		
		// 통신정보 설정
		HttpClient client = this.getClient(encType);
		
		// 접속을 수행하기 위한 객체
		HttpPost httppost = new HttpPost(pageUrl);
		
		// 업로드될 파일 정보가 있는 경우
		if (file != null) {
			MultipartEntity entity = new MultipartEntity();

			Log.d(TAG, "[UPLOAD INFO] " + file.getName() + "=" + file.getPath());
			entity.addPart(file.getName(), new FileBody(new File(file.getPath())));

			if (params != null) {
				for (int i=0; i<params.size(); i++) {
					String[] tmp = params.get(i);
					try {
						entity.addPart(tmp[0], new StringBody(tmp[1], Charset.forName(encType)));
					} catch (UnsupportedEncodingException e) {
						e.printStackTrace();
					}
				}
			}

			httppost.setEntity(entity);
		}
		// 업로드 파일 정보는 없고, 파라미터만 있는 경우.
		else {
			if (params != null) {
				int size = params.size();
				
				if (size > 0) {
					List<NameValuePair> nvp = new ArrayList<NameValuePair>();
					
					for (int i=0; i<size; i++) {
						String[] tmp = params.get(i);
						nvp.add(new BasicNameValuePair(tmp[0], tmp[1]));
					}
					
					try {
						UrlEncodedFormEntity encEntity = new UrlEncodedFormEntity(nvp, encType);
						httppost.setEntity(encEntity);
					} catch (UnsupportedEncodingException e) {}
				}
			}
		}
		
		try {
			// 요청을 보내고, 응답을 받는다.
			HttpResponse response = client.execute(httppost);

			// 서버의 정상 동작 여부 검사
			int resultCode = response.getStatusLine().getStatusCode();

			if (resultCode == HttpURLConnection.HTTP_OK) {
				// 수신된 응답에서 실 데이터를 추출한다.
				HttpEntity entity = response.getEntity();
				BufferedHttpEntity buffer = new BufferedHttpEntity(entity);

				// 추출한 데이터를 InputStream으로 변환
				istream = buffer.getContent();
			} else {
				// 서버 에러의 원인
				String reason = response.getStatusLine().getReasonPhrase();

				Log.e(TAG, "서버에러: " + resultCode);
				Log.e(TAG, reason);

			}
		} catch (ClientProtocolException e) {

			Log.e(TAG, "통신처리 에러: " + e.getLocalizedMessage());

			Log.e(TAG, e.getLocalizedMessage());
			Log.e(TAG, e.getStackTrace().toString());

		} catch (IOException e) {

			Log.e(TAG, "응답수신 에러: " + e.getLocalizedMessage());

			Log.e(TAG, e.getLocalizedMessage());
			Log.e(TAG, e.getStackTrace().toString());

		} catch (Exception e) {

			Log.e(TAG, "기타 통신 에러: " + e.getLocalizedMessage());

			Log.e(TAG, e.getLocalizedMessage());
			Log.e(TAG, e.getStackTrace().toString());

		} finally {
			// 통신 해제
			client.getConnectionManager().shutdown();
		}
		/********* 통신처리 끝 ********/
		
		Log.d(TAG, "=========== method end >> post");

		return istream;
	}
	
	public InputStream post(String url, FileInfo[] files) {
		return this.postMulti(url, ENC_TYPE, files);
	}
	
	public InputStream post(String url) {
		return this.postSingle(url, ENC_TYPE, null);
	}

	/** 기본 인코딩 타입을 사용하도록 재정의된 메서드 */
	public InputStream connect(String url) {
		InputStream is = this.post(url, null);
		return is;
	}
	
	public FileInfo createFile(String fname, String fpath) {
		return new FileInfo(fname, fpath);
	}

	public class FileInfo implements Serializable {
		
		private static final long serialVersionUID = 1L;
		
		private String m_szName = null;
		private String m_szPath = null;

		public FileInfo(String m_szName, String m_szPath) {
			super();
			this.m_szName = m_szName;
			this.m_szPath = m_szPath;
		}

		public String getName() {
			return m_szName;
		}

		public void setName(String m_szName) {
			this.m_szName = m_szName;
		}

		public String getPath() {
			return m_szPath;
		}

		public void setPath(String m_szPath) {
			this.m_szPath = m_szPath;
		}
	}
}
